﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Supermodel.DomainModel;
using Supermodel.Exceptions;
using Supermodel.MVC.Extensions;
using Supermodel.Reflection;
using Supermodel.Repository;

namespace Supermodel.MVC.FormModels
{
    public class DropdownFormModel : IComparable, IFormModelModelBinder, IFormModelEditorTemplate, IFormModelEntityMapper
    {
        public class Option
        {
            public Option(string value, string label)
            {
                Value = value;
                Label = label;
            }
            public string Value { get; private set; }
            public string Label { get; private set; }
        }
        public List<Option> Options = new List<Option>();

        public string SelectedValue { get; set; }
        public string SelectedLabel
        {
            get { return Options.Where(x => x.Value == SelectedValue).First().Label; }
        }
        public bool IsEmpty
        {
            get { return string.IsNullOrEmpty(SelectedValue); }
        }

        public List<SelectListItem> GetSelectListItemList()
        {
            var selectListItemList = new List<SelectListItem>();
            if (GetType() == typeof(DropdownFormModel)) selectListItemList.Add(new SelectListItem { Value = "", Text = "" });
            foreach (var option in Options)
            {
                var item = new SelectListItem {Value = option.Value, Text = option.Label};
                if (SelectedValue != null) item.Selected = (SelectedValue.CompareTo(option.Value) == 0);
                selectListItemList.Add(item);
            }
            return selectListItemList;
        }
        public override string ToString()
        {
            return SelectedValue;
        }
        public int CompareTo(object obj)
        {
            return SelectedValue.CompareTo(((DropdownFormModel)obj).SelectedValue);
        }

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            string key = bindingContext.ModelName;
            ValueProviderResult val = bindingContext.ValueProvider.GetValue(key);
            string attemptedValue;
            if (val == null || string.IsNullOrEmpty(val.AttemptedValue))
            {
                if (bindingContext.ModelMetadata.IsRequired) bindingContext.ModelState.AddModelError(key, string.Format("The field {0} is required", bindingContext.ModelMetadata.DisplayName ?? bindingContext.ModelMetadata.PropertyName));
                attemptedValue = "";
            }
            else
            {
                attemptedValue = val.AttemptedValue;
            }

            bindingContext.ModelState.SetModelValue(key, val);

            SelectedValue = attemptedValue;

            var existingModel = (DropdownFormModel)bindingContext.Model;
            if (existingModel != null)
            {
                existingModel.SelectedValue = SelectedValue;
                return existingModel;
            }
            return this;
        }

        public bool GetIEditorTemplateImplemented() { return true; }
        public MvcHtmlString EditorTemplate(HtmlHelper html)
        {
            return html.DropDownFormModelEditor();
        }

        public void MapFromEntityObject(object obj, Type objType)
        {
            if (!ReflectionHelpers.IsEntityType(objType)) throw new FormModelPropertyCantBeAutomapedException(string.Format("{0} can't be automapped to {1}", GetType().Name, objType.Name));
            
            Options = ((ISqlDataRepoGenericTypeIgnorant)RepoFactory.CreateForRuntimeType(objType)).GetDropdownOptions();

            var entity = (EntityCore)obj;
            SelectedValue = (entity == null) ? "" : entity.Id.ToString();
        }

        public object MapToEntityObject(object obj, Type objType)
        {
            if (!ReflectionHelpers.IsEntityType(objType)) throw new FormModelPropertyCantBeAutomapedException(string.Format("{0} can't be automapped to {1}", GetType().Name, objType.Name));

            if (string.IsNullOrEmpty(SelectedValue)) return null;

            var id = long.Parse(SelectedValue);
            var entity = (EntityCore)obj;
            if (entity != null && entity.Id == id) return entity;
               
            var repo = RepoFactory.CreateForRuntimeType(objType);
            var newEntity = (EntityCore)repo.ExecuteMethod("GetById", id);
            return newEntity;
        }
    }

    public class EnumDropdownFormModel<EnumT> : DropdownFormModel, IFormModelEntityMapper where EnumT : struct, IConvertible
    {
        public class EnumOption : Option
        {
            public EnumOption(EnumT value) : base(value.ToString(), value.GetDescription()) { }
            public EnumT EnumValue
            {
                get
                {
                    return (EnumT)Enum.Parse(typeof(EnumT), Value);
                }
            }
        }

        public EnumDropdownFormModel()
        {
            var enumValues = new List<object>();
            foreach (var item in Enum.GetValues(typeof(EnumT))) enumValues.Add(item);
            enumValues = enumValues.OrderBy(x => x.GetScreenOrder()).ToList();

            foreach (var option in enumValues) Options.Add(new EnumOption((EnumT)option));
            SelectedValue = enumValues[0].ToString();
        }

        public EnumT SelectedEnum
        {
            get
            {
                return (EnumT)Enum.Parse(typeof(EnumT), SelectedValue);
            }
            set
            {
                SelectedValue = value.ToString();
            }
        }

        new public void MapFromEntityObject(object obj, Type objType)
        {
            if (objType != typeof(EFCFEnum)) throw new FormModelPropertyCantBeAutomapedException(string.Format("{0} can't be automapped to {1}", GetType().Name, objType.Name));

            var enumObj = (EFCFEnum)obj;
            SelectedEnum = (EnumT)ReflectionHelpers.ExecuteStaticGenericMethod(typeof(EnumExtensions), "GetEnumValue", new[] { (Type)enumObj.PropertyGet("EnumType") }, enumObj);
        }

        new public object MapToEntityObject(object obj, Type objType)
        {
            if (objType != typeof(EFCFEnum)) throw new FormModelPropertyCantBeAutomapedException(string.Format("{0} can't be automapped to {1}", GetType().Name, objType.Name));

            var newEFCFObject = new EFCFEnum();
            ReflectionHelpers.ExecuteStaticGenericMethod(typeof(EnumExtensions), "SetEnumValue", new[] { GetType().GetGenericArguments()[0] }, newEFCFObject, SelectedEnum);
            return newEFCFObject;
        }
    }
}
